header{
#include <list>
#include <set>
#include <stdio.h>
#include <antlr/CharScanner.hpp>
}
options {
	language="Cpp";
    mangleLiteralPrefix = "KW_";
}

///////////////////Lexer//////////////////////////////

class PlantainLexer extends Lexer;

options {
    exportVocab = Plantain;
    testLiterals=false;
    k=2;
}

tokens{
    "as"; "and"; "nand"; "xor"; "nor"; "or"; "is"; "def"; "install"; "not"; "identically"; "in";
    FLOAT;
}
{
    int _lexlevel;

    std::list<std::list<std::string>* > _macronames;

    bool _macro_next;


    protected:
    void push_lexlevel(){

        if(_lexlevel == 0){
            maybe_init_macronames();
        }
        _lexlevel++;
        _macronames.push_front(new std::list<std::string>());
    }
    void pop_lexlevel(){

        _lexlevel--;
        std::list<std::string>* front = _macronames.front();
        _macronames.pop_front();
        delete front;
    }

    /**
     * Add name to be parsed as a macro for the rest of the scope.
     */
    void add_macroname(std::string name){
        maybe_init_macronames();
        _macronames.front()->push_front(name);
    }

    inline void maybe_init_macronames(){
        if(_macronames.empty()){
            _macro_next = false;
            _macronames.push_front(new std::list<std::string>());
        }
    }


    /**
     * Test if the given name is in the macro table in this scope
     */

    bool is_macro(std::string name){
        std::list<std::list<std::string>* >::iterator scope;
        maybe_init_macronames();
        for(scope = _macronames.begin(); scope != _macronames.end(); scope++){
            std::list<std::string>::iterator name_iter;
            for(name_iter = (*scope)->begin(); name_iter != (*scope)->end(); name_iter++){

                if(*name_iter == name) {
                    return true;
                }            
            }
        }
        return false;
    }

    /**
     * Test if the given name is in the macro table in the top level
     * of this scope.
     */

    bool is_macro_defined_here(std::string name){
        std::list<std::string>::iterator name_iter;
        maybe_init_macronames();
        for(name_iter = (*_macronames.begin())->begin(); name_iter != (*_macronames.begin())->end(); name_iter++){
            if(*name_iter == name) return true;
        }
        return false;
    }


    public:
	/** Test the token text against the literals table
	 * Override this method to perform a different literals test
	 */
	virtual int testLiteralsTable(int ttype) 
	{
        maybe_init_macronames();
		ANTLR_USE_NAMESPACE(std)map<ANTLR_USE_NAMESPACE(std)string,int,antlr::CharScannerLiteralsLess>::const_iterator i = 
            literals.find(text);


        if(is_macro(text)){
            //it's a macro, we should know so.
            return PlantainTokenTypes::MACRO;
        }

        if(_macro_next){
            _macro_next = false;
            if(!is_macro_defined_here(text)){
                //the macro should become defined here

                add_macroname(text);
            }else{
                //it is a redefinition, an error should happen
                //TODO: make an error happen
            }
        }

		if (i != literals.end())
			ttype = (*i).second;
        if (ttype == PlantainTokenTypes::KW_install){
            _macro_next = true;
        }else{
            _macro_next = false;
        }
		return ttype;
	}

	/** Test the text passed in against the literals table
	 * Override this method to perform a different literals test
	 * This is used primarily when you want to test a portion of
	 * a token
	 */
	virtual int testLiteralsTable(const ANTLR_USE_NAMESPACE(std)string& txt,int ttype) 
	{
		ANTLR_USE_NAMESPACE(std)map<ANTLR_USE_NAMESPACE(std)string,int,antlr::CharScannerLiteralsLess>::const_iterator i = literals.find(txt);
		if (i != literals.end())
			ttype = (*i).second;
		return ttype;
	}

}

WS_	:	(' '
        |'\t'
        |'\f'
        | (  "\n\r"
            |'\n'
            |'\r'
            ){newline();}
        )
		{ _ttype = ANTLR_USE_NAMESPACE(antlr)Token::SKIP; }
	;

//Delimiters

LPAREN:	'(' 
	;

RPAREN:	')' 
	;

LSQUARE: '['
    ;

RSQUARE: ']'
    ;

LCURLY:  '{' {push_lexlevel();}
    ;

RCURLY:  '}' {pop_lexlevel();}
    ;

STRQUOTE: '"'
    ;

SYMBQUOTE: '\''
    ;

//Assignment operators
ASSIGN: '='
    ;


//Comparison Operators
EQUAL: "=="
    ;

NEQ: "!="
    ;

LEQ:    "<="
    ;

GEQ:    ">="
    ;

LESS:     '<'
    ;

GREA:     '>'
    ;

//Arithmatic operators

STAR:	'*'
	;

DIV :   '/'
    ;

PLUS:	'+'
	;

MINUS:  '-'
    ;

MOD: '%'
    ;

BITAND: '&'
    ;

BITOR:  '|'
    ;

BITXOR: '^'
    ;

//Other stuff

SEMI:	';'
	;

COMMA:  ','
    ;

DOT:    '.'
    ;

SL_COMMENT
    : "//" (~('\n'|'\r'))* ('\n'|('\r'('\n')?)
                {newline();})?
        {_ttype = ANTLR_USE_NAMESPACE(antlr)Token::SKIP;}
    ;

// multiple-line comments
ML_COMMENT
    : "/*"
        (               /* '\r' '\n' can be matched in one alternative or by matching
                       '\r' in one iteration and '\n' in another. I am trying to
                       handle any flavor of newline that comes in, but the language
                       that allows both "\r\n" and "\r" and "\n" to all be valid
                       newline is ambiguous. Consequently, the resulting grammar
                       must be ambiguous. I'm shutting this warning off.
                    */
            options {
                generateAmbigWarnings=false;
            }
        :  { LA(2)!='/' }? '*'
        | '\r' '\n' {newline();}
        | '\r' {newline();}
        | '\n' {newline();}
        | ~('*'|'\n'|'\r')
        )*
        "*/"
        {_ttype = ANTLR_USE_NAMESPACE(antlr)Token::SKIP;}
    ;
        

protected
DIGIT
	:	'0'..'9'
	;


DOLLAR : '$';

protected
INTPART	:	(DIGIT)+
	;

INT : INTPART (FLOATPART {$setType(FLOAT);})?
    ;

FLOATPART : '.' INT {$setType(FLOAT);}
    ;

protected
LETTER
    : ('a'..'z') | ('A'..'Z')
    ;

protected
UNDERSCORE
    : '_'
    ;

ID options{testLiterals = true;}
    : (LETTER | UNDERSCORE) (LETTER | UNDERSCORE | DIGIT)*
    ;

MACRO
    : '\\'! ID
    ;

